#include "stdafx.h"

#include "MyControls/ConvertTool.h"	  
#include "MyControls/BezierEditor.h"

namespace My {

ConvertTool::ConvertTool(BezierEditor & owner)
	: m_Owner(owner)
{
	m_Hit.code = htNone;
}

void ConvertTool::Activate()
{
}

void ConvertTool::Deactivate()
{
}

void ConvertTool::PaintRect(CDC * pDC, const CRect & r)
{
}

void ConvertTool::OnLButtonDown(UINT nFlags, CPoint p)
{
	if (m_Owner.ControlPointAt(p, m_Hit.curve, m_Hit.node, m_Hit.control)) {
		m_Hit.code = htControlPoint;
		return;
	}
	if (m_Owner.NodeAt(p, m_Hit.curve, m_Hit.node)) {
		m_Hit.code = htNode;
		Bezier & b = m_Owner.GetBezierOwner()->GetBezier(m_Hit.curve);
		m_Hit.select = !b.IsSelected(m_Hit.node);
		if (m_Hit.select) {
			if ((GetAsyncKeyState(VK_SHIFT) & (~0 << 1)) == 0)
				m_Owner.GetBezierOwner()->DeselectAll();
			b.Select(m_Hit.node);
		}
		return;
	}
	if (m_Owner.CurveAt(p, m_Hit.curve, m_Hit.t)) {
		m_Hit.code = htCurve;
		return;
	}
	m_Hit.code = htNone;
}

void ConvertTool::OnLButtonDblClk(UINT nFlags, CPoint p)
{
}

void ConvertTool::OnLButtonUp(UINT nFlags, CPoint p)
{
	switch (m_Hit.code) {
		case htControlPoint: {
			Bezier & b = m_Owner.GetBezierOwner()->GetBezier(m_Hit.curve);
			BezierNode n = b.GetNode(m_Hit.node);
			if (n.type == bnCusp) 
				CuspToSmooth(n, m_Hit.control);
			else 
				n.type = bnCusp;
			b.SetNode(m_Hit.node, n);
			break;
		}
		case htNode: {
			Bezier & b = m_Owner.GetBezierOwner()->GetBezier(m_Hit.curve);
			if (!m_Hit.select && (GetAsyncKeyState(VK_SHIFT) & (~0 << 1)) != 0) {
				m_Owner.GetBezierOwner()->DeselectAll();
				b.Select(m_Hit.node);
			}
			bool flat = b.GetNode(m_Hit.node).type == bnFlat;
			for (Size i = 0; i < m_Owner.GetBezierOwner()->GetBezierCount(); ++i) {
				Bezier & b = m_Owner.GetBezierOwner()->GetBezier(i);
				if (!b.IsPartiallySelected())
					continue;
				for (Size j = 0; j < b.GetNodeCount(); ++j) {
					if (!b.IsSelected(j))
						continue;
					BezierNode n = b.GetNode(j);
					if (flat && n.type == bnFlat) {
						BezierNode prev = b.GetNode(Prev(b, j));
						BezierNode next = b.GetNode(Next(b, j));
						point<float> d = (next.position - prev.position) / 6;
						n.type = bnSymmetrical;
						n.control[0] = n.position - d;
						n.control[1] = n.position + d;
					} else {
						n.type = bnFlat;
						n.control[0] = n.control[1] = n.position;
					}
					b.SetNode(j, n);
				}
			}
			break;
		}
		case htCurve: {
			Bezier & b = m_Owner.GetBezierOwner()->GetBezier(m_Hit.curve);
			if ((GetAsyncKeyState(VK_SHIFT) & (~0 << 1)) == 0)
				m_Owner.GetBezierOwner()->DeselectAll();
			b.Select(b.Insert(m_Hit.t));
			break;
		}
		case htNone:
			if ((GetAsyncKeyState(VK_SHIFT) & (~0 << 1)) == 0)
				m_Owner.GetBezierOwner()->DeselectAll();
			break;
	}
	m_Hit.code = htNone;
}

void ConvertTool::OnRButtonDown(UINT nFlags, CPoint p)
{
}

void ConvertTool::OnRButtonDblClk(UINT nFlags, CPoint p)
{
}

void ConvertTool::OnRButtonUp(UINT nFlags, CPoint p)
{
}

void ConvertTool::OnMouseMove(UINT nFlags, CPoint p)
{
	switch (m_Hit.code) 
	{
		case htControlPoint: {
			Bezier & b = m_Owner.GetBezierOwner()->GetBezier(m_Hit.curve);
			BezierNode n = b.GetNode(m_Hit.node);
			if (n.type == bnCusp)
				CuspToSmooth(n, m_Hit.control);
			else 
				n.type = bnCusp;
			m_Owner.GetBench().SetManipulator(
				std::auto_ptr<Manipulator>(new DragControlPoint(
				m_Owner,
				b,
				m_Hit.node, 
				m_Hit.control,
				p,
				&n)));
			m_Hit.code = htNone;
			break;
		}
		case htNode: {
			Bezier & b = m_Owner.GetBezierOwner()->GetBezier(m_Hit.curve);
			BezierNode n = b.GetNode(m_Hit.node);
			n.type = bnSymmetrical;
			n.control[0] = n.control[1] = n.position;
			m_Owner.GetBench().SetManipulator(
				std::auto_ptr<Manipulator>(new DragControlPoint(
				m_Owner,
				b,
				m_Hit.node, 
				1,
				p,
				&n)));
			m_Hit.code = htNone;
			break;
		}
		case htCurve: {
			Bezier & b = m_Owner.GetBezierOwner()->GetBezier(m_Hit.curve);
			if ((GetAsyncKeyState(VK_SHIFT) & (~0 << 1)) == 0)
				m_Owner.GetBezierOwner()->DeselectAll();
			Size i = b.Insert(m_Hit.t);
			b.Select(i);
			BezierNode n = b.GetNode(i);
			n.type = bnCusp;
			n.control[1] = n.position;
			m_Owner.GetBench().SetManipulator(
				std::auto_ptr<Manipulator>(new DragControlPoint(
					m_Owner,
					b,
					i, 
					1,
					p,
					&n)));
			m_Hit.code = htNone;
			break;
		}
	}
}

void ConvertTool::OnMouseEnter(UINT nFlags, CPoint p)
{
}

void ConvertTool::OnMouseLeave()
{
}

void ConvertTool::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
}

void ConvertTool::OnKeyUp(UINT nChar, UINT nRepCnt, UINT nFlags)
{
}

bool ConvertTool::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)
{
	SetCursor(GetApp().LoadCursor("CURSOR_CONVERT"));
	return true;
}

void ConvertTool::CuspToSmooth(BezierNode & n, Size c) const
{
	n.type = bnSmooth;
	Size c0 = c, c1 = 1 - c;
	float s0 = (float) abs(n.control[c0] - n.position);
	if (s0 != 0) {
		float s = (float) abs(n.control[c1] - n.position) / s0;
		n.control[c1] = n.position + (n.position - n.control[c0]) * s;
	}
}

} //namespace My
